#include "bChunk.h"
#include "bRegex.h"
#include "bString.h"
#include "bObject.h"
#include <iostream>
#include <string>
#include <exception>
#include <cctype>

using namespace Regex;

unsigned long int Chunk::gID = 0;

Repetition::Repetition(const char* ptr):_min(0),_max(0)
{
	switch(ptr[0])
	{
	case '*':
		_min = 0;
		_max = 0;
		break;
	case '+':
		_min = 1;
		_max = 0;
		break;
	case '?':
		_min = 0;
		_max = 1;
	}
	std::cout << "New repetition: " << _min << "::" << _max << std::endl;
}

Repetition::~Repetition(){}

const unsigned long int Repetition::min() const { return _min; }

const unsigned long int Repetition::max() const { return _max; }

Chunk::Chunk():mID(++gID)
{
	//std::cout << "new " << this << std::endl;
}

Chunk* Chunk::modify(Repetition* ptr)
{
	modifier = ptr;
	return this;
}

Chunk::~Chunk()
{
	//std::cout << "~" << this << std::endl;
}

GroupChunk::GroupChunk(const char* ptr,unsigned long int len)
{
	unsigned long int p = 0;
	if(ptr[p]=='(')
		p++;
	if(ptr[p]=='?')
		p++;
	bString target(ptr+p,len-p);
	std::cout << "Begin group " << std::endl;
	comp = new Regex(target);
	std::cout << "End group " << std::endl;
}

unsigned long int GroupChunk::operator () (const char* ptr,unsigned long int pos) const
{
	unsigned long int i(pos);
	return comp->match(ptr+pos,true);
}

GroupChunk::~GroupChunk()
{
}

CharClassChunk::CharClassChunk(const char * ptr, unsigned long int l){
	unsigned long int p=0;
	if(ptr[p]=='[')
		p++;//Ignore the open bracket.
	include = ptr[p]!='^';
	if(!include)
		p++; //Ignore the exclusion sign.
	l-=p;
	chars = new bString(ptr+p,l);
	if(!caseSensitive)
		for(unsigned long int i=0;i<l;i++)
			(*chars.object())[i] = tolower((*chars.object())[i]);
	std::cout << (include?"Inclusive":"Exclusive") << "Character class Chunk: " << chars << std::endl;
}

unsigned long int CharClassChunk::operator () (const char* ptr,unsigned long int pos) const
{
	const char c = caseSensitive?(*(ptr+pos)):(tolower(*(ptr+pos)));
	if(include)
	{
		try
		{
			chars->find(c);
			return 1;
		}
		catch(std::exception e)
		{
			bString msg("Character '");
			msg.append(ptr+pos,1);
			msg.append("' not found in character class '");
			msg.append(chars.object()->operator const char *());
			msg.append("'.");
			throw std::exception(msg);
		}
	}
	else
	{
		try
		{
			chars->find(c);
		}
		catch(std::exception e)
		{
			return 1;
		}
		bString msg("Character '");
		msg.append(ptr+pos,1);
		msg.append("' not allowed in character class '");
		msg.append(chars.object()->operator const char *());
		msg.append("'.");
		throw std::exception(msg);
	}
}

unsigned long int TerminateChunk::operator() (const char* ptr,unsigned long int pos) const
{
	if( ptr[pos] != '\0')
	{
		bString b("Non-terminated string at ");
		b.append(ptr+pos);
		throw std::exception(b);
	}
	return 1;
}

ConstChunk::ConstChunk(const char* ptr,unsigned long int l)
{
	if(!caseSensitive)
		for(unsigned long int i=0;i<l;i++)
			comp.append(tolower(ptr[i]));
	else comp.append(ptr,l);
	std::cout << "Const Chunk: '" << comp << "'\n";
}

static bool charDiff(const char a,const char b)
{
	char c=tolower(a),d=tolower(b);
	if(caseSensitive) c=a,d=b;
	return (c^d) != 0;
}

unsigned long int ConstChunk::operator()(const char* ptr,unsigned long int pos) const
{
	unsigned long int min=0,max=0;
	bool modified = false;
	try
	{
		min=modifier->min();
		max=modifier->max();
		modified = true;
	} catch ( ... ) {}
	unsigned long int endPoint = comp.length()-1;//Subtract one because terminator byte, two if we have a repetition mod.
	if(modified)
		endPoint--;
	for(unsigned long int i=0;i<endPoint;i++)
		if(charDiff(ptr[pos+i],comp[i]))
			throw std::exception("Const chunk mis-match.");
	if(modified)
	{
		unsigned long int ret = 0;
		for(int i=endPoint;!charDiff(comp[endPoint],ptr[pos+i]);i++)
			ret++;
		if(max>min)
			ret = ret>max?max:ret;
		else if(ret<min)
			throw std::exception("Not enough characters.");
		return endPoint + ret;
	}
	return endPoint;
}